package com.menghao.rpc.config;

import com.menghao.rpc.RpcConstants;
import com.menghao.rpc.provider.handle.tcp.RpcRequestMsgHandler;
import com.menghao.rpc.netty.TcpMessageHandler;
import com.menghao.rpc.netty.TcpServer;
import com.menghao.rpc.provider.handle.http.HttpProviderEntrance;
import com.menghao.rpc.provider.handle.tcp.ExecutionExecutor;
import com.menghao.rpc.consumer.handle.tcp.RpcResponseMsgHandler;
import com.menghao.rpc.provider.regisiter.ProviderRegister;
import com.menghao.rpc.provider.regisiter.ProviderRepository;
import com.menghao.rpc.spring.BeansManager;
import com.menghao.rpc.spring.ProviderPostProcessor;
import com.menghao.rpc.spring.RpcRequestConvert;
import com.menghao.rpc.spring.RpcResponseConvert;
import com.menghao.rpc.zookeeper.CuratorClient;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean;
import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty;
import org.springframework.boot.autoconfigure.web.ServerProperties;
import org.springframework.boot.context.properties.EnableConfigurationProperties;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.http.MediaType;
import org.springframework.http.converter.HttpMessageConverter;
import org.springframework.util.Assert;
import org.springframework.web.servlet.config.annotation.WebMvcConfigurerAdapter;

import java.util.ArrayList;
import java.util.List;

/**
 * <p>服务提供方自配置类.</br>
 *
 * @author MarvelCode
 */
@Configuration
@EnableConfigurationProperties(RpcProperties.class)
@ConditionalOnProperty(prefix = "marvel.rpc", name = "providerEnable", havingValue = "true")
public class ProviderAutoConfig extends WebMvcConfigurerAdapter {

    private static final Logger LOGGER = LoggerFactory.getLogger(ConsumerAutoConfig.class);

    private RpcProperties rpcProperties;

    public ProviderAutoConfig(RpcProperties rpcProperties) {
        Assert.hasText(rpcProperties.getZkServerHost(), "please appoint zookeeper server address");
        this.rpcProperties = rpcProperties;
    }

    @Override
    public void configureMessageConverters(List<HttpMessageConverter<?>> converters) {
        converters.add(new RpcRequestConvert(MediaType.TEXT_PLAIN));
        converters.add(new RpcResponseConvert(MediaType.TEXT_PLAIN));
    }

    @Bean
    public ProviderRepository providerRepository() {
        return new ProviderRepository();
    }

    @Bean
    public ProviderPostProcessor postProcessor(ProviderRepository providerRepository) {
        return new ProviderPostProcessor(providerRepository);
    }


    @Bean
    @ConditionalOnMissingBean(BeansManager.class)
    public BeansManager beansManager() {
        return BeansManager.getInstance();
    }

    @Bean(destroyMethod = "close")
    @ConditionalOnMissingBean(CuratorClient.class)
    public CuratorClient curatorClient() {
        return new CuratorClient(rpcProperties.getZkServerHost(),
                rpcProperties.getSessionTimeout(), rpcProperties.getConnectionTimeout(),
                rpcProperties.getZkRootPath());
    }

    @Configuration
    @ConditionalOnProperty(prefix = "marvel.rpc", name = "type", havingValue = "http")
    @EnableConfigurationProperties(ServerProperties.class)
    public static class HttpModeAutoConfig {

        private ServerProperties serverProperties;

        @Autowired
        public HttpModeAutoConfig(ServerProperties serverProperties) {
            this.serverProperties = serverProperties;
        }

        @Bean
        public HttpProviderEntrance providerEntrance(ProviderRepository providerRepository) {
            LOGGER.info(RpcConstants.LOG_RPC_PREFIX + "service provider use type-http");
            return new HttpProviderEntrance(providerRepository);
        }

        @Bean
        public ProviderRegister providerRegister(CuratorClient curatorClient) {
            return new ProviderRegister(curatorClient, serverProperties.getPort());
        }
    }


    @Configuration
    @ConditionalOnProperty(prefix = "marvel.rpc", name = "type", havingValue = "tcp")
    @EnableConfigurationProperties(RpcTcpProperties.class)
    public static class TcpModeAutoConfig {

        private RpcTcpProperties tcpProperties;

        @Autowired
        public TcpModeAutoConfig(RpcTcpProperties tcpProperties) {
            this.tcpProperties = tcpProperties;
        }

        @Bean(initMethod = "start", destroyMethod = "close")
        public TcpServer nettyTcpServer() {
            List<TcpMessageHandler> messageHandlers = new ArrayList<>(2);
            messageHandlers.add(new RpcRequestMsgHandler());
            messageHandlers.add(new RpcResponseMsgHandler());
            return new TcpServer(
                    tcpProperties.getExceptPort(),
                    tcpProperties.getMaxFrameLength(),
                    tcpProperties.getReadIdle(),
                    tcpProperties.getWritIdle(), messageHandlers);
        }

        @Bean
        public ProviderRegister providerRegister(CuratorClient curatorClient, TcpServer nettyTcpServer) {
            return new ProviderRegister(curatorClient, nettyTcpServer.getPort());
        }

        @Bean
        public ExecutionExecutor executionExecutor() {
            return new ExecutionExecutor(5, 10, 60, 300);
        }
    }
}
